package onlinebanking.client;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.swing.JOptionPane;

import onlinebanking.constants.*;
import onlinebanking.network.*;
import onlinebanking.security.probabilistic.GMReinforcedNetworkSwitch;

/**
 * 
 * @author Del Mundo Simple Client. Command prompt only. No GUI.
 */
public class SimpleClient {
	// TODO wrap in a GUI.
	// pero ginagawa na LELZ

	private NetworkSwitch networkSwitch = null;
	private ServerListener serverlistener = null;
	private boolean isConnected = false;
	private boolean isCommandBased = false;
	
	private String SERVER_IP = Network.SERVER_IP;
	private int SERVER_PORT = Network.SERVER_PORT;

	/**
	 * Creates an instance of a SimpleClient.
	 * 
	 * @throws Exception
	 */
	public SimpleClient() throws Exception {
		String ip = JOptionPane.showInputDialog(null, "Enter IP address of Server:", "Enter IP", JOptionPane.QUESTION_MESSAGE);
		String portStr = JOptionPane.showInputDialog(null, "Enter PORT number of Server:", "Enter port", JOptionPane.QUESTION_MESSAGE);
		
		try {
			SERVER_IP = (ip != null || ip.equals(""))? ip: Network.SERVER_IP;
			SERVER_PORT = Integer.parseInt(portStr);
		} catch (Exception e) {
			SERVER_IP = Network.SERVER_IP;
			SERVER_PORT = Network.SERVER_PORT;
		}
		
		
		connectToServer();
	}

	// tester only- methods to be called by the GUI Client
	public static void main(String[] args) throws Exception {
		System.out.println("SimpleClient-main");
		new SimpleClient();
	}

	/**
	 * Gets the ServerListener of the SimpleClient.
	 * 
	 * @return the ServerListener of the SimpleClient
	 */
	public ServerListener getServerListener() {
		return this.serverlistener;
	}

	/**
	 * Connects the SimpleClient to the Server.
	 * 
	 * @throws IOException
	 *             Thrown when an error occured while creating the socket.
	 */
	private void connectToServer() throws IOException {
		System.out.println("SimpleClient: Connecting to server...");
		Socket socket = null;
		// TODO READ SERVER IP AND SERVERPORT FROM CONFIG FILE
		try {
			socket = new Socket(SERVER_IP, SERVER_PORT);
			//add encryption/decryption to networkswitch
			this.networkSwitch = new SimpleNetworkSwitch(socket);
			NetworkSwitch pointer = this.networkSwitch;
			
			try {
				this.networkSwitch = new GMReinforcedNetworkSwitch(this.networkSwitch);
			} catch (IOException e) {
				this.networkSwitch = pointer;
			}
			
			this.serverlistener = new ServerListener(this, networkSwitch);
			this.isConnected = true;
			this.serverlistener.start();

			System.out.println("SimpleClient:Connected to server");

			runClient();
		} catch (UnknownHostException e) {

			// IP of host cannot be determined
			System.out.println("SimpleClient:Cannot find server.");
		} catch (IOException e) {

			// error in creating socket
			System.out.println("SimpleClient:Error in creating Socket");
			throw new IOException();
		}
	}

	/**
	 * Runs the SimpleClient. This method is only used in command-line clients.
	 */
	public void runClient() {
		String outgoingmsg = null;
		if (!isCommandBased) {
			System.out.println("SimpleClient: TextCommands disabled"); // TODO:
			return;
		}
		do {
			outgoingmsg = getInput("Enter Command:");
			interpretCommand(outgoingmsg);
		} while (!outgoingmsg.equals("/quit"));
	}

	// for console use

	/**
	 * Processes a command-line command.
	 */
	private void interpretCommand(String message) {

		String msgarr[] = message.split(" ");
		String cmd = msgarr[0];

		if (cmd.equals("/quit")) {
			disconnect();
		} else if (cmd.equals("/login")) {
			login(msgarr[1], msgarr[2]);
		} else if (cmd.equals("/logout")) {
			logout();
		} else if (cmd.equals("/listofBA")) {
			getListOfMyBankAccounts();
		} else if (cmd.equals("/selectBA")) {
			selectActiveBankAccount(msgarr[1]);
		} else if (cmd.equals("/balance")) {
			inquireBalance();
		} else if (cmd.equals("/initfundstransfer")) {
			double ammountTransfer = Double.valueOf(msgarr[1]);
			String destAccountno = msgarr[2];
			long PAC = Long.parseLong(msgarr[3]);
			long ConfirmPAC = Long.parseLong(msgarr[4]);
			int expDay = Integer.parseInt(msgarr[5]);
			int expMonth = Integer.parseInt(msgarr[6]);
			int expYear = Integer.parseInt(msgarr[7]);
			initFundsTransfer(ammountTransfer, destAccountno, PAC, ConfirmPAC,
					expDay, expMonth, expYear);
		} else if (cmd.equals("/confirmtrans")) {
			int transactionid = Integer.parseInt(msgarr[1]);
			long PAC = Long.parseLong(msgarr[2]);
			long ConfirmPAC = Long.parseLong(msgarr[3]);

			confirmTransaction(transactionid, PAC, ConfirmPAC);
		} else if (cmd.equals("/viewpending")) {
			viewPendingTransactions();
		}
	}

	/**
	 * Get an input (command line) from the user.
	 * 
	 * @param msg
	 * @return
	 */
	public String getInput(String msg) {
		System.out.println(msg);
		Scanner scan = new Scanner(System.in);
		return scan.nextLine();
		// return null;
	}

	/***
	 * ======================== HANGING METHODS TO BE CALLED BY
	 * GUI-WRAPPER=====================================
	 **/
	/**
	 * Disconnects the client.
	 */
	public void disconnect() {
		Message message = new Message();
		message.setMsgType(MSG.DISCONNECT_REQUEST);
		this.sendToNetwork(message);
	}

	/**
	 * Logs in the SimpleClient
	 * 
	 * @param username
	 *            The name of the user.
	 * @param password
	 *            The password of the user.
	 */
	public void login(String username, String password) {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.LOGIN_REQUEST);
		message.addAttribute("username", username);
		message.addAttribute("password", password);
		this.sendToNetwork(message);
		System.out.println("SimpleClient logging in...");
	}

	/**
	 * Logs out the SimpleClient.
	 */
	public void logout() {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.LOGOUT_REQUEST);
		this.sendToNetwork(message);
	}

	/**
	 * Requests announcements from server
	 */
	public void getAnnouncements(){
		if(!isConnected) 
			return;
		System.out.println("simpleclient: announcement req");
		Message message = new Message();
		message.setMsgType(MSG.ANNOUNCEMENTS);
		this.sendToNetwork(message);		
	}
	
	/**
	 * Requests the list of the user's Bank Accounts from the Server.
	 */
	public void getListOfMyBankAccounts() {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.MY_BANK_ACCOUNTS);
		message.setMsgSubType(MSG.LIST_OF_BANKACCOUNTS);
		this.sendToNetwork(message);
	}

	/**
	 * Lets the user to select his/her active bank account.
	 * 
	 * @param accountno
	 *            The ID of the Bank Account
	 */
	public void selectActiveBankAccount(String accountno) {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.MY_BANK_ACCOUNTS);
		message.setMsgSubType(MSG.SELECT_ACTIVE_ACCOUNT);
		message.addAttribute("active_accountno", accountno);
		this.sendToNetwork(message);
	}

	/**
	 * Requests the server for the account balance.
	 */
	public void inquireBalance() {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.BALANCE_REQUEST);
		this.sendToNetwork(message);
	}

	/**
	 * 
	 * @param ammountTransfer
	 * @param destAccountno
	 * @param PAC
	 * @param ConfirmPAC
	 * @param expDay
	 * @param expMonth
	 * @param expYear
	 */
	public void initFundsTransfer(double ammountTransfer, String destAccountno,
			long PAC, long ConfirmPAC, int expDay, int expMonth, int expYear) {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.INIT_FUNDSTRANSFER);
		message.addAttribute("ammountTransfer", ammountTransfer);
		message.addAttribute("destAccountno", destAccountno);
		message.addAttribute("PAC", PAC);
		message.addAttribute("ConfirmPAC", ConfirmPAC);
		message.addAttribute("expDay", expDay);
		// expmonth = java.util.calendar.MONTH
		message.addAttribute("expMonth", expMonth);
		message.addAttribute("expYear", expYear);
		this.sendToNetwork(message);
	}

	public void confirmTransaction(int transactionid, long PAC, long ConfirmPAC) {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.CONFIRM_TRANSACTION);
		message.addAttribute("transactionid", transactionid);
		message.addAttribute("CconfirmPAC", ConfirmPAC);
		message.addAttribute("PAC", PAC);
		this.sendToNetwork(message);
	}

	public void viewPendingTransactions() {
		if (!isConnected)
			return;
		Message message = new Message();
		message.setMsgType(MSG.VIEW_TRANSACTIONS);
		message.setMsgSubType(MSG.LIST_OF_PENDING_TRANSACTIONS);
		this.sendToNetwork(message);
	}

	public void viewPendingTransactionDetails(int transactionid) {
		if (!isConnected) {
			return;
		}
		Message message = new Message();
		message.setMsgType(MSG.VIEW_TRANSACTIONS);
		message.setMsgSubType(MSG.DETAIL_SPECIFIC_TRANSACTION);
		message.addAttribute("transactionid", transactionid);
		this.sendToNetwork(message);
		System.out.println("requesting details for transaction # "
				+ transactionid);
	}

	public void requestStatementOfAccount() {

	}

	private void sendToNetwork(Message message) {
		String messageString = message.toString();
		this.networkSwitch.send(messageString);
	}
}
